
import * as obvm from '../runtime/vm.mjs'
import CodePointLib from '../runtime/codePoint.mjs'
import TextLib from '../runtime/text.mjs'
import * as obdebugger from '../runtime/debugger.mjs'
import * as VirtualServerLib from './virtualServerLib.mjs'
import * as Vue from 'vue';
let codepoint = new CodePointLib();
let text = new TextLib();
let nativeLibs = [
    codepoint.install.bind(codepoint),
    text.install.bind(text),
    VirtualServerLib.default.install
];
export class VirtualServer {
    /**
     * 检测长时间运行
     */
    frameStart;
    loadedScript;
    timeoutMap;
    systemLogLevel = 5;
    userLogLevel = 5;
    vm;
    constructor(scriptArrayBuffer, scene) {
        let that = this;
        this.scene = scene;
        this.loadedScript = obvm.ScriptLoader.loadScript(scriptArrayBuffer, nativeLibs, {
            instructionWrap(instruction) {
                let pausable = obdebugger.Debugger.pausableInstructionWrap(instruction);
                return (st, func, local, i) => {
                    if (Date.now() - that.frameStart > 3000) {
                        st.fsm.VM.pause();
                        throw new obvm.VMPausedException('func_timeout');
                    }
                    return pausable.apply(null, [st, func, local, i]);
                };
            }
        });
    }
    start() {
        let vmdebugger = new obdebugger.Debugger({
            // broker: vmdebuggerConnector
        });
        this.timeoutMap = new Map();
        this.vm = new obvm.VM(this.loadedScript, {
            debugger: vmdebugger,
            setTimeout: setTimeout.bind(window), Output: this.debugLog.bind(this),
        });
        this.vm.__scene = this.scene;
        this.vm.usrLogLevel = 0;
        let fsmname = this.loadedScript.entryFSMName;
        let fsm = this.vm.CreateFSM(fsmname);
        if (!fsm) {
            this.debugLog("No FSM named " + fsmname);
        } else {
            this.update();
        }
    }
    stop() {
        if (this.timeoutMap) {
            this.timeoutMap.forEach((value, key) => {
                clearTimeout(value);
            });
            this.timeoutMap = null;
        }
        this.vm.Destroy();
        this.vm = null;
    }
    update() {
        let vm = this.vm;
        if (vm && vm.isRunning()) {
            this.frameStart = Date.now();
            try {
                if (vm.update()) {
                    requestAnimationFrame(this.update.bind(this));
                }
            } catch (e) {
                console.error(e);
                this.debugLog(e.message);
            }
        }
    }
    debugLog(msg, type, level, stackpath, block) {
        if ((type == 'usr' && level >= this.userLogLevel)
            || level >= this.systemLogLevel) {
            console.log({ type, level, msg, block, stackpath })
        }
    }
}


let UI = Vue.defineAsyncComponent(async () => {
    await OpenBlock.onInitedPromise();
    return {
        name: "VirtualServerUI",
        template: (await axios.get('/openblock/jsruntime/server/VirtualServer.html')).data,
        data() {
            return {
                enabled: true,
                sceneName: "",
                miniMode: true,
                controller: null,
                running: false
            };
        },
        watch: {
            enabled: {
                handler(newV, oldV) {
                    if (!newV) {
                        OB_IDE.removeComponent(this);
                        this.stop();
                        this.controller.dispatchEvent(new CustomEvent('closed'));
                    }
                }
            },
            controller: {
                handler() {
                    this.running = this.controller?.server?.vm?.isRunning();
                }
            }
        },
        computed: {
            isRunning() {
                return this.controller?.server?.vm?.isRunning();
            }
        },
        methods: {
            stop() {
                this.controller.stop();
                this.running = this.controller?.server?.vm?.isRunning();
            },
            start() {
                this.controller.start();
                this.running = this.controller?.server?.vm?.isRunning();
            }
        },
    };
});
export class VirtualServerUI extends EventTarget {
    server;
    constructor(scene, scriptArrayBuffer) {
        super();
        this.scene = scene;
        this.scriptArrayBuffer = scriptArrayBuffer;
        OB_IDE.addComponent(UI).then(ui => {
            this.ui = ui;
            ui.controller = (this);
            ui.sceneName = scene.name;
        });
    }
    start() {
        this.stop();
        this.server = new VirtualServer(this.scriptArrayBuffer, this.scene);
        this.server.start();
        this.dispatchEvent(new CustomEvent('ServerStart', {
            detail: {}
        }));
    }
    stop() {
        if (this.server) {
            this.server.stop();
            this.dispatchEvent(new CustomEvent('ServerStop', {
                detail: {}
            }));
            this.server = null;
        }
    }
}